package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper accountFreezeMapper;


    //try
    @Override
    @Transactional
    public void deduct(String userId, int money) {
        String xid = RootContext.getXID();//获取事务id
        /*
        避免业务悬挂判断:
        判断accountFreezeMapper.selectById(xid)结果是否有冻结记录,如果有,一定是cancel执行过,要拒绝执行try方法
         */
        AccountFreeze oldFreeze = accountFreezeMapper.selectById(xid);
        if (oldFreeze != null) {
            //说明cancel执行过,拒绝执行try方法
            return;
        }

        //1.扣减可用余额(由于account_tbl中  `money` int(11) unsigned DEFAULT '0',默认就是unsigned的,故金额不会变成负数,一旦减成负数自动操作失败回滚,故无需判断,直接扣减余额即可)
        accountMapper.deduct(userId, money);
        //2.记录冻结金额,记录事务状态
        AccountFreeze accountFreeze = new AccountFreeze();
        accountFreeze.setUserId(userId);
        accountFreeze.setFreezeMoney(money);
        accountFreeze.setState(AccountFreeze.State.TRY);
        accountFreeze.setXid(xid);
        accountFreezeMapper.insert(accountFreeze);
    }


    //confirm
    @Override
    @Transactional
    public boolean confirm(BusinessActionContext businessActionContext) {
        /*
        删除业务天生具有幂等性,无论删除多少次都没相当于删除了一次,无需手动实现幂等判断
         */

        //1.获取事务id
        String xid = businessActionContext.getXid();
        //2.根据id删除冻记录
        int count = accountFreezeMapper.deleteById(xid);//count是删除的条数
        return count == 1;
    }


    //cancel
    @Override
    @Transactional
    public boolean cancel(BusinessActionContext businessActionContext) {
        //0.查询冻结记录获取userId和money  或者   从businessActionContext获取
        String xid = businessActionContext.getXid();
        AccountFreeze accountFreeze = accountFreezeMapper.selectById(xid);



        /*
        1.进行空回滚判断:判断accountFreezeMapper.selectById(xid)结果是否为null,为null证明try没有执行,需要空回滚
         */
        if (accountFreeze == null) {//空回滚并不是直接return true而是要模拟真的回滚,在account_freeze_tbl表中插入一条数据来记录执行过回滚操作但是,并不真的恢复原account_tbl表的数据
            AccountFreeze accountFreeze1 = new AccountFreeze();
            String userId = businessActionContext.getActionContext("userId").toString();//从businessActionContext获得try方法的形参
            accountFreeze1.setUserId(userId);
            accountFreeze1.setFreezeMoney(0);//金额置零
            accountFreeze1.setState(AccountFreeze.State.CANCEL);//事务状态标记为CANCEL表示执行了回滚
            accountFreeze1.setXid(xid);
            accountFreezeMapper.insert(accountFreeze1);
            return true;
        }

        /*
        2.幂等判断,使得cancel方法无论被RM执行几次将来只会生效一次
         */
        if (accountFreeze.getState() == AccountFreeze.State.CANCEL) {
            //说明已经处理过一次CANCEL了,无需重复处理
            return true;
        }


        //1.恢复可用余额
        String userId = accountFreeze.getUserId();
        Integer freezeMoney = accountFreeze.getFreezeMoney();
        accountMapper.refund(userId, freezeMoney);
        //2.将冻结金额清零,状态改为cancel
        accountFreeze.setFreezeMoney(0);
        accountFreeze.setState(AccountFreeze.State.CANCEL);
        int count = accountFreezeMapper.updateById(accountFreeze);//count是修改的条数
        return count == 1;
    }
}
